/*
 * Jenny Ruppen 09-730-391
 * Sebastien Goldberg 08-914-855
 * Henry Raymond 08-928-061
 * Stefan Mussato 09-752-270
 */
 
package ch.uzh.ifi.sopra.gruppe9.game;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collections;


/**
 * Server-side class for the action location "tower element market", where you 
 * can buy tower elements. Implements IActionLocation.
 */
@SuppressWarnings("serial")
public class TowerElementMarket extends UnicastRemoteObject implements IRmiActionLocation {

	
	/**
	 * Game board
	 */
	private IGameBoard gameBoard;
	
	
	/**
	 * Type of tower elements sold in the market (base, middle, window or roof)
	 */
	public TowerElement.Type type;
	
	
	/**
	 * Non-visible pile of elements that cannot be bought (except through bribery).
	 */
	private ArrayList<TowerElement> pile = new ArrayList<TowerElement>();
	
	
	/**
	 * Visible selection of elements that can be bought.
	 */
	private ArrayList<TowerElement> selection = new ArrayList<TowerElement>();
	
	
	/**
	 * Array of the slots where buying agent cards can be placed. 
	 * (0 = no card, 1 = one card in the color of the color constraint, 2 = two cards)
	 */
	private int[] buyingAgentSlots;
	
	
	/**
	 * Color constraint for buying agent cards in this shop.
	 */
	private BuyingAgent.Color colorConstraint = BuyingAgent.Color.NONE;
	
	
	/**
	 * Constructor: Sets up tower element market, according to its type.
	 */
	public TowerElementMarket(IGameBoard gameBoard, TowerElement.Type type) throws RemoteException {
		super();
		
		this.gameBoard = gameBoard;
		
		this.type = type;
		pile = TowerElement.getElementPileForShop(type);
		Collections.shuffle(pile); // shuffle tower elements pile
		
		if(type == TowerElement.Type.BASE) {
			buyingAgentSlots = new int[5];
			
		} else if(type == TowerElement.Type.MIDDLE) {
			buyingAgentSlots = new int[4];
			
		} else if(type == TowerElement.Type.WINDOW) {
			buyingAgentSlots = new int[5];
			
		} else if(type == TowerElement.Type.ROOF) {
			buyingAgentSlots = new int[4];
		}
		
		for(int i = 0; i < buyingAgentSlots.length; i++) {
			buyingAgentSlots[i] = 0;
		}
	}

	
	/**
	 * Fill up the selection of tower elements with elements from the pile
	 */
	public void refillSelection() {
		int selectionSize = 7; // Number of elements in the selection
		if(type == TowerElement.Type.WINDOW) {
			selectionSize = 8;
		}
		while(selection.size() < selectionSize && pile.size() > 0) {
			selection.add(pile.get(0));
			pile.remove(0);
		}
	}

	
	/**
	 * Returns array with IDs of all tower elements in the selection of this market.
	 */
	@Override
	public int[] getTowerElementSelectionIDs() throws RemoteException {
		int[] ids = new int[selection.size()];
		for(int i = 0; i < selection.size(); i++) {
			ids[i] = selection.get(i).getId();
		}
		return ids;
	}
	
	
	/**
	 * Returns array with IDs of all tower elements in the pile of this market.
	 */
	@Override
	public int[] getTowerElementPileIDs() throws RemoteException {
		int[] ids = new int[pile.size()];
		for(int i = 0; i < pile.size(); i++) {
			ids[i] = pile.get(i).getId();
		}
		return ids;
	}

	
	/**
	 * Returns array of the slots where buying agent cards can be placed. 
	 * (0 = no card, 1 = one card in the color of the color constraint, 2 = two cards)
	 */
	@Override
	public int[] getBuyingAgentSlots() throws RemoteException {
		return buyingAgentSlots;
	}
	
	
	/**
	 * Returns true if there is still at least one free buying agent card slot.
	 */
	public boolean hasFreeBuyingAgentSlot() throws RemoteException {
		for(int slot : getBuyingAgentSlots()) {
			if(slot == 0) {
				return true;
			}
		}
		return false;
	}

	
	/**
	 * Returns id of color of color constraint. (see enumToIntTranslator in class BuyingAgent)
	 */
	@Override
	public int getColorConstraint() throws RemoteException {
		return BuyingAgent.enumToInt(colorConstraint);
	}

	
	/**
	 * Buys tower element with ID (parameter1) for player (playerIndex)
	 * If only one buying agent card is used: buyingAgentID2 = -1 
	 */
	@Override
	public boolean doAction(int playerIndex, int buyingAgentID1, int buyingAgentID2, int parameter1) throws RemoteException {
		
		IPlayer player = gameBoard.getPlayer(playerIndex);
		
		if(!hasFreeBuyingAgentSlot()) { // Has no free buying agent slot
			System.err.println("Error: Market has no free buying agent slot.");
			return false;
		}
		
		if(buyingAgentID2 == -1) { // One buying agent card
			
			if(colorConstraint != BuyingAgent.Color.NONE) { // Color constraint
				if(new BuyingAgent(buyingAgentID1).getColor() != colorConstraint) {
					System.err.println("Error: Color of buying agent card does not comply with color constraint.");
					return false;
				}
			} else {
				colorConstraint = new BuyingAgent(buyingAgentID1).getColor(); // Set color constraint
			}
			
			if(!player.removeBuyingAgent(buyingAgentID1)) {
				System.err.println("Error: Player doesn't have given buying agent card. (Buying agent ID: " + buyingAgentID1 + ")");
				return false;
			}
			
			// Put one buying agent cards on the next free slot
			for(int i = 0; i < buyingAgentSlots.length; i++) {
				if(buyingAgentSlots[i] == 0) {
					buyingAgentSlots[i] = 1;
					break;
				}
			}
			
			
		} else { // Two buying agent cards
			
			if(colorConstraint == BuyingAgent.Color.NONE) { // No Color constraint
				System.err.println("Error: Cannot give two cards when there is no color constraint.");
				return false;
			}
			
			if(!player.removeBuyingAgent(buyingAgentID1)) {
				System.err.println("Error: Player doesn't have first given buying agent card. (Buying agent ID: " + buyingAgentID1 + ")");
				return false;
			}
			
			if(!player.removeBuyingAgent(buyingAgentID2)) {
				System.err.println("Error: Player doesn't have second given buying agent card. (Buying agent ID: " + buyingAgentID2 + ")");
				return false;
			}
			
			// Put two buying agent cards on the next free slot
			for(int i = 0; i < buyingAgentSlots.length; i++) {
				if(buyingAgentSlots[i] == 0) {
					buyingAgentSlots[i] = 2;
					break;
				}
			}
		}
		
		// Create new tower element for the player's inventory
		TowerElement towerElement = new TowerElement(parameter1);
		
		// Check if the player has enough money
		if(!player.checkMoney(towerElement.getCost())) {
			System.err.println("Error: Player doesn't have enough money.");
			return false;
		}
		
		// Subtract the cost from the player's money balance
		player.payMoney(towerElement.getCost());
		
		// Remove tower element from market's selection
		for(int i = 0; i < selection.size(); i++) {
			if(selection.get(i).getId() == parameter1) {
				selection.remove(i);
				break;
			}
		}
		
		// Add the tower element to the player's inventory
		player.addElementToInventory(towerElement);
		
		gameBoard.reportUpdateMoneyBuyingAgentsInventory();
		
		// It's the next player's turn
		if(type == TowerElement.Type.BASE) {
			gameBoard.nextPlayersTurn(3, 1);			
		} else if(type == TowerElement.Type.MIDDLE) {
			gameBoard.nextPlayersTurn(3, 2);	
		} else if(type == TowerElement.Type.WINDOW) {
			gameBoard.nextPlayersTurn(3, 3);
		} else if(type == TowerElement.Type.ROOF) {
			gameBoard.nextPlayersTurn(3, 4);
		}
		
		return true;
	}
	
	
	/**
	 * Used for house of spies only!
	 * buyingAgentID1 and buyingAgentID2 are not used, as the buying agents are removed by the house of spies.
	 * parameter2 is not used and its only purpose is to determine that the call is made by the house of spies.
	 * if parameter1 is -1, the player abstained from buying a tower element
	 */
	@Override
	public boolean doAction(int playerIndex, int buyingAgentID1, int buyingAgentID2, int parameter1, int parameter2) throws RemoteException {
		
		IPlayer player = gameBoard.getPlayer(playerIndex);		
		
		if(parameter1 != -1) {
			// Create new tower element for the player's inventory
			TowerElement towerElement = new TowerElement(parameter1);
		
			// Check if the player has enough money
			if(!player.checkMoney(towerElement.getCost())) {
				System.err.println("Error: Player doesn't have enough money.");
				return false;
			}
		
			// Subtract the cost from the player's money balance
			player.payMoney(towerElement.getCost());
		
			// Remove tower element from market's pile
			for(int i = 0; i < pile.size(); i++) {
				if(pile.get(i).getId() == parameter1) {
					pile.remove(i);
					break;
				}
			}
		
			// Add the tower element to the player's inventory
			player.addElementToInventory(towerElement);
		}
		
		gameBoard.reportUpdateMoneyBuyingAgentsInventory();
		
		// It's the next player's turn
		if(type == TowerElement.Type.BASE) {
			gameBoard.nextPlayersTurn(4, 1);			
		} else if(type == TowerElement.Type.MIDDLE) {
			gameBoard.nextPlayersTurn(4, 2);	
		} else if(type == TowerElement.Type.WINDOW) {
			gameBoard.nextPlayersTurn(4, 3);
		} else if(type == TowerElement.Type.ROOF) {
			gameBoard.nextPlayersTurn(4, 4);
		}
		
		return true;
	}
	
	
	/**
	 * Empties all buying agent slots.
	 */
	public void emptyBuyingAgentSlots() {
		colorConstraint = BuyingAgent.Color.NONE;
		for(int i = 0; i < buyingAgentSlots.length; i++) {
			buyingAgentSlots[i] = 0;
		}
	}
	
	
	/**
	 * Unused
	 */
	@Override
	public boolean doAction(int playerIndex, int buyingAgentID1, int buyingAgentID2) throws RemoteException {
		return false;
	}

	/**
	 * Unused
	 */
	@Override
	public boolean doAction(int playerIndex, int buyingAgentID1, int buyingAgentID2, int parameter1, String parameter2) throws RemoteException {
		return false;
	}

}
